package fun.yao.common.db;

import fun.yao.common.Constant;
import fun.yao.common.uitls.LogUtil;

import java.sql.*;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 封闭原始的Connection
 * @author csl
 *
 */
public class GessConnection implements Connection
{
    //所属连接池
    protected ConnPool mConnPool = null ;

    //连接索引号
    protected int mIndex = -1 ;

    //真实的数据库连接
    protected Connection dbConn = null ;

    //缓存被创建过的 Statement
    protected ArrayList<Statement> stmtList = new ArrayList<Statement>();

    //连接获取时间
    protected long mGetTime = 0 ;

    //上次数据库连接探测时间
    protected long mLastSqlTestTime = System.currentTimeMillis();

    //连接的创建时间
    protected long mCreateTime = System.currentTimeMillis();

    //当前连接索引号
    private volatile static int mStaticIndex = 0 ;

    /**
     * 构造函数
     * @param dbConn 真实的数据库连接
     */
    public GessConnection(ConnPool pool,Connection dbConn)
    {
        this.mConnPool = pool ;
        this.dbConn = dbConn ;
        this.mIndex = mStaticIndex++;

        //防止同时到期，有意修改每个连接的创建时间
        this.mCreateTime = this.mCreateTime + (System.nanoTime()%100000) ;

    }

    /**
     * 释放相关资源，防止泄漏
     */
    public void freeStatement()
    {
        if ( stmtList.size() > 0 )
        {

            if ( stmtList.size() > 3 )
                DbLog.writeLog( "连接" + this.toString() + "曾创建过[" + stmtList.size() + "]个Statement!." );

            //释放Statement
            for ( int i = 0 ; i < stmtList.size() ; i++ )
            {
                Statement stmt = stmtList.get(i);
                try
                {
                    stmt.close();
                }catch(Exception e)
                {
                    DbLog.writeLog(e);
                }

            }

            //清空Statement列表
            stmtList.clear();

            //如果曾执行过SQL，则当前释放时间记为上次SQL探测时间
            this.mLastSqlTestTime = System.currentTimeMillis();

        }

    }

    /**
     * 获取数据库连接池的名称
     * @return
     */
    public ConnPool getConnPool()
    {
        return this.mConnPool;
    }

    /**
     * 设置 获取连接时间
     */
    public void setGetTime()
    {
        this.mGetTime = System.currentTimeMillis();
        //DbLog.writeLog( "获取连接" + this.toString() + "：时间=" + this.mGetTime);
    }

    /**
     * 设置 释放时间
     */
    public void setFreeTime()
    {
        long iInvTime = System.currentTimeMillis() - this.mGetTime ;
        if ( iInvTime > 2 )
            DbLog.writeLog( "释放连接" + this.toString() + "，占用时间=" + iInvTime + "毫秒" );
    }

    /**
     * 检查是否需要进行SQL探测
     * @param iInvTime 定期探测的间隔时间（秒）
     */
    public boolean checkIsNeedSqlTest(int iInvTime)
    {
        long iCurrInvTime =  System.currentTimeMillis() - this.mLastSqlTestTime ;
        if ( iCurrInvTime > iInvTime * 1000 )
            return true;
        else
            return false;
    }

    /**
     * 检查连接是否已过期
     * @param iOvertime 过期时间（秒）
     * @return
     */
    public boolean checkIsOvertime(int iOvertime)
    {
        long iCurrInvTime =  System.currentTimeMillis() - this.mCreateTime ;
        if ( iCurrInvTime > iOvertime * 1000 )
            return true;
        else
            return false;
    }


    /**
     * 使用SQL测试数据库连接
     * @return
     * @throws SQLException
     */
    public boolean execSqlTest(String sql) throws SQLException
    {
        Statement stmt = null;

        try
        {
            stmt = dbConn.createStatement();
            stmt.executeQuery(sql);
            this.mLastSqlTestTime = System.currentTimeMillis();
            return true;
        }catch(Exception e)
        {
            DbLog.writeLog(LogUtil.getExpStack(e));
        }finally
        {
            if ( stmt != null )
                stmt.close();
        }

        return false;

    }

    public String toString()
    {
        return "[index=" + this.mIndex + "]";
    }


    //-------------------------  以下是实现Connection接口的函数

    public void clearWarnings() throws SQLException
    {
        dbConn.clearWarnings();
    }

    public void close() throws SQLException
    {
        try
        {
            dbConn.close();
        }catch(Exception e)
        {
            LogUtil.WriteLog(Constant.NORMAL_ERROR, e);
        }
    }

    public void commit() throws SQLException
    {
        dbConn.commit();
    }

    public Statement createStatement() throws SQLException
    {
        Statement stmt = dbConn.createStatement();
        stmtList.add(stmt);
        return stmt;
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency)
            throws SQLException
    {
        Statement stmt = dbConn.createStatement(resultSetType, resultSetConcurrency);
        stmtList.add(stmt);
        return stmt;
    }

    public Statement createStatement(int resultSetType,
                                     int resultSetConcurrency, int resultSetHoldability)
            throws SQLException
    {
        Statement stmt= dbConn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
        stmtList.add(stmt);
        return stmt;
    }

    public boolean getAutoCommit() throws SQLException
    {
        return dbConn.getAutoCommit();
    }

    public String getCatalog() throws SQLException
    {
        return dbConn.getCatalog();
    }

    public int getHoldability() throws SQLException
    {
        return dbConn.getHoldability();
    }

    public DatabaseMetaData getMetaData() throws SQLException
    {
        return dbConn.getMetaData();
    }

    public int getTransactionIsolation() throws SQLException
    {
        return dbConn.getTransactionIsolation();
    }

    public Map<String, Class<?>> getTypeMap() throws SQLException
    {
        return dbConn.getTypeMap();
    }

    public SQLWarning getWarnings() throws SQLException
    {
        return dbConn.getWarnings();
    }

    public boolean isClosed() throws SQLException
    {
        return dbConn.isClosed() ;
    }

    public boolean isReadOnly() throws SQLException
    {
        return dbConn.isReadOnly();
    }

    public String nativeSQL(String sql) throws SQLException
    {
        return dbConn.nativeSQL(sql);
    }

    public CallableStatement prepareCall(String sql) throws SQLException
    {
        CallableStatement cstmt = dbConn.prepareCall(sql);
        stmtList.add(cstmt);
        return cstmt;
    }

    public CallableStatement prepareCall(String sql, int resultSetType,
                                         int resultSetConcurrency) throws SQLException
    {
        CallableStatement cstmt = dbConn.prepareCall(sql, resultSetType, resultSetConcurrency);
        stmtList.add(cstmt);
        return cstmt;
    }

    public CallableStatement prepareCall(String sql, int resultSetType,
                                         int resultSetConcurrency, int resultSetHoldability)
            throws SQLException
    {
        CallableStatement cstmt = dbConn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        stmtList.add(cstmt);
        return cstmt;
    }

    public PreparedStatement prepareStatement(String sql) throws SQLException
    {
        PreparedStatement pstmt = dbConn.prepareStatement(sql);
        stmtList.add(pstmt);
        return pstmt;
    }

    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
            throws SQLException
    {
        PreparedStatement pstmt = dbConn.prepareStatement(sql, autoGeneratedKeys);
        stmtList.add(pstmt);
        return pstmt;
    }

    public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
            throws SQLException
    {
        PreparedStatement pstmt = dbConn.prepareStatement(sql, columnIndexes);
        stmtList.add(pstmt);
        return pstmt;
    }

    public PreparedStatement prepareStatement(String sql, String[] columnNames)
            throws SQLException
    {
        PreparedStatement pstmt = dbConn.prepareStatement(sql, columnNames);
        stmtList.add(pstmt);
        return pstmt;
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType,
                                              int resultSetConcurrency) throws SQLException
    {
        PreparedStatement pstmt = dbConn.prepareStatement(sql, resultSetType, resultSetConcurrency);
        stmtList.add(pstmt);
        return pstmt;
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType,
                                              int resultSetConcurrency, int resultSetHoldability)
            throws SQLException
    {
        PreparedStatement pstmt = dbConn.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        stmtList.add(pstmt);
        return pstmt;
    }

    public void releaseSavepoint(Savepoint savepoint) throws SQLException
    {
        dbConn.releaseSavepoint(savepoint);
    }

    public void rollback() throws SQLException
    {
        dbConn.rollback();
    }

    public void rollback(Savepoint savepoint) throws SQLException
    {
        dbConn.rollback(savepoint);
    }

    public void setAutoCommit(boolean autoCommit) throws SQLException
    {
        dbConn.setAutoCommit(autoCommit);
    }

    public void setCatalog(String catalog) throws SQLException
    {
        dbConn.setCatalog(catalog);
    }

    public void setHoldability(int holdability) throws SQLException
    {
        dbConn.setHoldability(holdability);
    }

    public void setReadOnly(boolean readOnly) throws SQLException
    {
        dbConn.setReadOnly(readOnly);
    }

    public Savepoint setSavepoint() throws SQLException
    {
        return dbConn.setSavepoint();
    }

    public Savepoint setSavepoint(String name) throws SQLException
    {
        return dbConn.setSavepoint(name);
    }

    public void setTransactionIsolation(int level) throws SQLException
    {
        dbConn.setTransactionIsolation(level);
    }

    public void setTypeMap(Map<String, Class<?>> map) throws SQLException
    {
        dbConn.setTypeMap(map);
    }

    public Array createArrayOf(String typeName, Object[] elements)
            throws SQLException {
        return dbConn.createArrayOf(typeName, elements);
    }

    public Blob createBlob() throws SQLException {
        return dbConn.createBlob();
    }

    public Clob createClob() throws SQLException {
        return dbConn.createClob();
    }

    public NClob createNClob() throws SQLException {
        return dbConn.createNClob();
    }

    public SQLXML createSQLXML() throws SQLException {
        return dbConn.createSQLXML();
    }

    public Struct createStruct(String typeName, Object[] attributes)
            throws SQLException {
        return dbConn.createStruct(typeName, attributes);
    }


    public void setSchema(String schema) throws SQLException {
        dbConn.setSchema(schema);
    }

    public String getSchema() throws SQLException {
        return dbConn.getSchema();
    }

    public void abort(Executor executor) throws SQLException {
        dbConn.abort(executor);
    }

    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
        dbConn.setNetworkTimeout(executor, milliseconds);
    }

    public int getNetworkTimeout() throws SQLException {
        return dbConn.getNetworkTimeout();
    }

    public Properties getClientInfo() throws SQLException {
        return dbConn.getClientInfo();
    }

    public String getClientInfo(String name) throws SQLException {
        return dbConn.getClientInfo(name);
    }

    public boolean isValid(int timeout) throws SQLException {
        return dbConn.isValid(timeout);
    }

    public void setClientInfo(Properties properties)
            throws SQLClientInfoException {
        dbConn.setClientInfo(properties);

    }

    public void setClientInfo(String name, String value)
            throws SQLClientInfoException {
        dbConn.setClientInfo(name, value);

    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return dbConn.isWrapperFor(iface);
    }

    public <T> T unwrap(Class<T> iface) throws SQLException {
        return dbConn.unwrap(iface);
    }

}
